// 通过通道实现的流水线生成素数
// 采用 Sieve of Eratosthenes 生成素数
// ./exam_sieve_test [last] [n]
#include "thread.h"
#include "chan.h"

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

#define NUM 256

#define isprime(arr, n, val) { \
    int i; \
    for (i = 0; arr[i] && n%arr[i] != 0; i++) \
        ; \
    val = arr[i] == 0; \
}

struct args {
    Chan_T c;
    int n, last;
};

static int
source(void *arg)
{
    struct args *p = arg;
    int i = 2;
    if (Chan_send(p->c, &i, sizeof(i))) {
        for (i = 3; Chan_send(p->c, &i, sizeof(i));)
            i += 2;
    }
    return 0;
}

static void
filter(int *primes, Chan_T input, Chan_T output)
{
    int x;
    for (;;) {
        Chan_receive(input, &x, sizeof(x));
        int is;
        isprime(primes, x, is);
        if (is) {
            if (Chan_send(output, &x, sizeof(x)) == 0)
                break;
        }
    }
    Chan_receive(input, &x, 0); // Notify send receiving is over
}

static int
sink(void *arg)
{
    struct args *p = arg;
    Chan_T input = p->c;
    int i = 0, x, primes[NUM];

    primes[0] = 0;
    for (;;) {
        Chan_receive(input, &x, sizeof(x));
        int is;
        isprime(primes, x, is);
        if (is) {
            if (x > p->last)
                break;

            assert(i < NUM-1);
            primes[i++] = x;
            primes[i] = 0;
            printf(" %d", x);

            if (i == p->n) {
                p->c = Chan_new();
                Thread_new(sink, p, sizeof(*p));
                filter(primes, input, p->c);
                return 0;
            }
        }
    }

    printf("\n");
    Chan_receive(input, &x, 0);
    return 0;
}

int main(int argc, char **argv)
{
    struct args ar;
    ar.n = 5;
    ar.last = 1000;
    if (argc >= 3)
        ar.n = atoi(argv[2]);
    if (argc >= 2)
        ar.last = atoi(argv[1]);

    assert(ar.n > 0 && ar.n < NUM);
    assert(ar.last > 0);
    Thread_init(0);

    ar.c = Chan_new();

    Thread_new(source, &ar, (int)sizeof(ar));
    Thread_new(sink, &ar, (int)sizeof(ar));

    Thread_exit(0);
    printf("main exit now!\n");
    return 0;
}

